class CanvasManager {
    constructor() {
        // 获取canvas元素及2D绘图上下文
        this.canvas = document.getElementById('canvas');
        this.ctx = this.canvas.getContext('2d');
        // 初始化状态变量（绘制状态、Ctrl键状态、坐标记录）
        this.isDrawing = false;
        this.isCtrlPressed = false;
        this.lastX = 0;
        this.lastY = 0;

        this.initCanvas(); // 配置canvas基础设置
        this.initEventListeners(); // 绑定鼠标/键盘事件
        this.addOperationHint(); // 添加操作提示信息
    }

    initCanvas() {
        this.ctx.fillStyle = 'black'; // 设置画布背景为黑色
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // 填充画布背景
        this.ctx.lineWidth = 15; // 设置画笔宽度为15
        this.ctx.lineCap = 'round'; // 设置画笔端点为圆形
        this.ctx.strokeStyle = 'white'; // 设置画笔颜色为白色
    }

    initEventListeners() {
        // 键盘事件
        document.addEventListener('keydown', (e) => this.handleKeyDown(e));
        document.addEventListener('keyup', (e) => this.handleKeyUp(e));

        // 鼠标事件
        this.canvas.addEventListener('mousedown', (e) => this.startDrawing(e));
        this.canvas.addEventListener('mousemove', (e) => this.draw(e));
        this.canvas.addEventListener('mouseup', () => this.stopDrawing());
        this.canvas.addEventListener('mouseout', () => this.stopDrawing());
    }

    handleKeyDown(e) {
        if (e.key === 'Control') this.isCtrlPressed = true;
    }

    handleKeyUp(e) {
        if (e.key === 'Control') this.isCtrlPressed = false;
    }

    startDrawing(e) {
        if (this.isCtrlPressed && e.button === 0) {
            this.isDrawing = true;
            [this.lastX, this.lastY] = [e.offsetX, e.offsetY];
        }
    }

    draw(e) {
        if (!this.isDrawing) return;

        this.ctx.beginPath();
        this.ctx.moveTo(this.lastX, this.lastY);
        this.ctx.lineTo(e.offsetX, e.offsetY);
        this.ctx.stroke();
        [this.lastX, this.lastY] = [e.offsetX, e.offsetY];
    }

    stopDrawing() {
        this.isDrawing = false;
    }

    clearCanvas() {
        this.ctx.fillStyle = 'black';
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    }

    addOperationHint() {
        const hint = document.createElement('div');
        hint.className = 'operation-hint';
        hint.innerHTML = '使用方式：<br>按住Ctrl+左键拖动';
        this.canvas.parentNode.insertBefore(hint, this.canvas.nextSibling);
    }
}

// 初始化Canvas管理器
const canvasManager = new CanvasManager();

/**
 * 异步函数，用于将画布内容发送至预测接口并处理响应。
 * 无参数，无返回值，通过调用handlePredictionResponse处理预测结果或错误。
 */
async function predictCanvas() {
    try {
        /**
         * 将canvas画面转为Data URL编码
         * 通过POST请求将编码数据发送至/predict接口
         * 使用application/x-www-form-urlencoded格式传输
         * 解析返回的JSON数据并调用处理函数
         */
        // 将画布内容转换为Data URL格式
        const dataUrl = canvasManager.canvas.toDataURL();

        // 向/predict端点发送POST请求，传输编码后的画布数据
        const response = await fetch('/predict', {
            method: 'POST',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            body: `canvas=${encodeURIComponent(dataUrl)}`
        });

        // 解析响应JSON并传递给处理函数
        handlePredictionResponse(await response.json());
    } catch (error) {
        // 网络错误或请求失败时传递错误信息
        handlePredictionResponse({error: '预测请求失败'});
    }
}


/**
 * 异步处理文件上传并请求预测结果。
 * 该函数从文件输入控件获取用户选择的文件，验证文件存在性后，
 * 将文件通过POST请求发送至预测接口，并处理返回的JSON响应。
 *
 * 参数：
 * 无
 *
 * 返回值：
 * 无（返回一个Promise，但未显式返回值）
 */
async function predictUpload() {
    // 获取文件输入控件并验证文件选择状态
    const fileInput = document.getElementById('fileInput');
    if (!fileInput.files.length) {
        alert('请选择要上传的文件');
        return;
    }

    try {
        // 构建包含上传文件的表单数据
        const formData = new FormData();
        formData.append('file', fileInput.files[0]);

        // 发送POST请求到/predict端点并处理响应
        const response = await fetch('/predict', {
            method: 'POST',
            body: formData
        });
        handlePredictionResponse(await response.json());
    } catch (error) {
        // 处理上传过程中发生的错误
        handlePredictionResponse({error: '文件上传失败'});
    }
}


/**
 * 处理预测响应数据并更新页面元素
 * @param {Object} data - 包含预测结果或错误信息的对象
 * @param {string} [data.error] - 错误信息（存在时表示请求失败）
 * @param {string} [data.prediction] - 预测结果文本
 * @param {number} [data.confidence] - 预测置信度百分比
 * @returns {void} 无返回值，直接操作DOM更新界面
 */
function handlePredictionResponse(data) {
    const predElem = document.getElementById('predValue');
    const confElem = document.getElementById('confValue');

    /**
     * 当响应包含错误信息时，更新页面显示错误状态和具体错误原因
     */
    if (data.error) {
        predElem.textContent = '错误';
        confElem.textContent = data.error;
        return;
    }

    /**
     * 当响应成功时，更新页面显示预测结果和置信度信息
     */
    predElem.textContent = data.prediction;
    confElem.textContent = data.confidence;
}


// 初始化训练日志
fetch('/logs')
    .then(response => response.json())
    .then(data => {
        if (data.error) {
            console.error(data.error);
            return;
        }

        // 更新训练日志
        document.getElementById('bestLoss').textContent = data.best_val_loss || '-';
        document.getElementById('finalLr').textContent = data.final_lr || '-';
        document.getElementById('trainAcc').textContent =
            data.train_acc ? `${(data.train_acc.slice(-1)[0] * 100).toFixed(4)}%` : '-';
        document.getElementById('valAcc').textContent =
            data.val_acc ? `${(data.val_acc.slice(-1)[0] * 100).toFixed(4)}%` : '-';

        // 更新测试结果
        document.getElementById('testLoss').textContent = data.test_loss || '-';
        document.getElementById('testAcc').textContent = data.test_acc || '-';

        // 更新混淆矩阵
        if (data.confusion_matrix) {
            document.getElementById('confusionMatrix').src = data.confusion_matrix + '?t=' + Date.now();
        }

        // 绘制损失和准确率曲线
        if (data.epochs && data.train_loss && data.val_loss) {
            Plotly.newPlot('lossChart', [
                {x: data.epochs, y: data.train_loss, name: '训练损失', mode: 'lines'},
                {x: data.epochs, y: data.val_loss, name: '验证损失', mode: 'lines'},
                {x: data.epochs, y: data.train_acc, name: '训练准确率', mode: 'lines', yaxis: 'y2'},
                {x: data.epochs, y: data.val_acc, name: '验证准确率', mode: 'lines', yaxis: 'y2'}
            ], {
                title: '训练过程可视化',
                xaxis: {title: '训练轮数'},
                yaxis: {title: '损失值', range: [0, Math.max(...data.train_loss, ...data.val_loss)]},
                yaxis2: {
                    title: '准确率 (%)',
                    overlaying: 'y',
                    side: 'right',
                    range: [0, 1],
                    tickformat: '.0%', // 百分比显示
                    hoverformat: '.4%' // 悬停时显示两位小数
                },
                legend: {
                    x: 1.05, y: 0.5, // 右侧居中
                    orientation: 'v', // 垂直排列
                    bgcolor: 'rgba(255,255,255,0.9)'
                },
                margin: {r: 150} // 增加右侧边距
            });
        }
    })
    .catch(error => console.log('日志加载失败:', error));

function clearCanvas() {
    canvasManager.clearCanvas();
}